package postgres

import (
	"context"
	"fmt"
	"github.com/lib/pq"
	"gitlab.local/backend/proto/dto"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"manager/client/grpc"
	"manager/config"
	"manager/errors"
	"manager/lib/trace_log"
	"manager/model"
	"manager/model/entity"
	"manager/store/redis"
	"manager/utils"
	"strings"
	"time"
)

var Ebike = &ebike{}

type ebike struct{}

// CreateInBatch 批量创建
func (a *ebike) CreateInBatch(ctx context.Context, m []*entity.Ebike) error {
	return GetDB(ctx).Table("ebikes").Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "sn_code"}},
		DoNothing: true,
	}).CreateInBatches(m, len(m)).Error
}

func (a *ebike) Create(ctx context.Context, m *entity.Ebike) (int64, error) {
	err := GetDB(ctx).Create(m).Error
	return m.ID, err
}
func (a *ebike) ExecTx(ctx context.Context, fn func(ctx context.Context) error) error {
	return GetDB(ctx).Transaction(func(tx *gorm.DB) error {
		ctx = context.WithValue(ctx, DBCONTEXTKEY, tx)
		return fn(ctx)
	})
}

func (a *ebike) Find(ctx context.Context, id int64) (*entity.Ebike, error) {
	e := &entity.Ebike{}
	err := db.WithContext(ctx).Model(&entity.Ebike{}).First(&e, id).Error
	return e, err
}

func (a *ebike) FindBySnCode(ctx context.Context, sn string) (*entity.Ebike, error) {
	e := &entity.Ebike{}
	err := db.WithContext(ctx).Model(&entity.Ebike{}).Where("sn_code = ?", sn).First(&e).Error
	return e, err
}

func (a *ebike) Update(ctx context.Context, id int64, dict map[string]interface{}) error {
	return db.WithContext(ctx).Model(&entity.Ebike{}).Where("id = ?", id).Updates(dict).Error
}

func (a *ebike) Delete(ctx context.Context, id int64) error {
	return db.WithContext(ctx).Delete(&entity.Ebike{}, id).Error
}

func (a *ebike) List(ctx context.Context, in *model.EbikeListRequest) (int, []*entity.Ebike, error) {
	var (
		q      = db.WithContext(ctx).Model(&entity.Ebike{})
		err    error
		total  int64
		ebikes []*entity.Ebike
	)

	if len(in.BikeIds) > 0 {
		q = q.Where("id in (?)", in.BikeIds)
	}

	if err = q.Count(&total).Error; err != nil {
		return 0, nil, err
	}
	if err = q.Order("created_at desc").Limit(in.Size).Offset((in.Index - 1) * in.Size).Find(&ebikes).Error; err != nil {
		return 0, nil, err
	}
	return int(total), ebikes, nil
}

func (a *ebike) WebList(ctx context.Context, in *model.EbikeListRequest) (int64, []*model.EbikesInfoResponse, error) {
	var (
		err    error
		total  int64
		ebikes = make([]*model.EbikesInfoResponse, 0)
	)

	sql := "ebikes.id,ebikes.distribute_time,ebikes.control_id, ebikes.sn_code,ebikes.frame_code,ebikes.engine_code,ebikes.color,ebikes.battery_id ,ebikes.status,ebikes.store_id,ebikes.stock as stock_status,ebikes.is_bind,ebikes.created_at,ebikes.bind_users," +
		"ebike_types.name device_type_name,ebike_types.id type_id,ebike_types.socket_type," +
		"user_ebikes.user_id,user_ebikes.is_manager,user_ebikes.created_at as bind_time," +
		"controls.id as control_id, controls.firmware_id, controls.firmware_version," +
		"user_bike_settings.ebike_name as name"
	q := db.Model(&entity.Ebike{}).Select(sql).Joins("left join ebike_types on ebikes.type_id = ebike_types.id")
	q = q.Joins("left join user_ebikes  on ebikes.id = user_ebikes.ebike_id and user_ebikes.is_manager = 1")
	q = q.Joins("left join controls on ebikes.control_id = controls.id")
	q = q.Joins("left join user_bike_settings on ebikes.id = user_bike_settings.ebike_id and user_ebikes.user_id=user_bike_settings.user_id")
	if in.TypeID != nil {
		q = q.Where(" ebikes.type_id = ?", *in.TypeID)
	}
	if in.SNCode != "" {
		q = q.Where("ebikes.sn_code like ?", utils.SqlLike(&in.SNCode))
	}

	if len(in.SNCodes) > 0 {
		var (
			conds  []string
			values []interface{}
		)
		for _, sn := range in.SNCodes {
			conds = append(conds, "ebikes.sn_code LIKE ?")
			values = append(values, sn)
		}

		q = q.Where(strings.Join(conds, " OR "), values...)
	}

	if in.IsBind != nil {
		q = q.Where("ebikes.is_bind = ?", *in.IsBind)
	}
	if in.Status != nil {
		q = q.Where("ebikes.status = ?", *in.Status)
	}
	if in.FrameCode != "" {
		q = q.Where("ebikes.frame_code like ?", "%"+in.FrameCode+"%")
	}
	if in.BindStart != 0 {
		q = q.Where("user_ebikes.created_at >= ?", in.BindStart) //车辆绑定表中的创建时间
	}
	if in.BindEnd != 0 {
		q = q.Where("user_ebikes.created_at <= ?", in.BindEnd) // 车辆绑定表中的创建时间
	}

	if len(in.UserIds) > 0 {
		q = q.Where("user_ebikes.user_id IN ?", in.UserIds)
	}

	if in.UserId != nil {
		q = q.Where("user_ebikes.user_id = ?", *in.UserId)
	}

	if in.EbikeId != nil {
		q = q.Where("ebikes.id = ?", in.EbikeId)
	}

	if len(in.BikeIds) > 0 {
		q = q.Where("ebikes.id in ?", in.BikeIds)
	}

	if len(in.TypeIds) > 0 {
		q = q.Where("ebikes.type_id in ?", in.TypeIds)
	}

	if in.TypeName != nil && *in.TypeName != "" {
		q = q.Where("ebike_types.name like ?", utils.SqlLike(in.TypeName))
	}

	if len(in.TypeNames) > 0 {
		q = q.Where("ebike_types.name in ?", in.TypeNames)
	}

	if len(in.Statuses) > 0 {
		q = q.Where("ebikes.status in ?", in.Statuses)
	}

	if in.Stock != nil {
		q = q.Where("ebikes.stock = ?", *in.Stock)
	}

	if len(in.SearchTimeZone) > 1 {
		q = q.Where("ebikes.created_at >= ? and ebikes.created_at <= ?", in.SearchTimeZone[0], in.SearchTimeZone[1])
	}

	b := !in.OnlyCount && !in.OnlyList
	if in.OnlyCount || b {
		if err = q.Debug().Count(&total).Error; err != nil {
			return total, nil, err
		}
	}

	if in.OnlyList || b {
		err = q.Limit(in.Size).Offset((in.Index - 1) * in.Size).Order("ebikes.created_at desc").Scan(&ebikes).Error
	}

	return total, ebikes, err
}

func (a *ebike) Count(ctx context.Context) (*model.EbikeCountResponse, error) {
	var (
		err error
		out model.EbikeCountResponse
	)
	// 查询车辆总数和已经绑定的车辆数
	str := "SELECT COUNT(*) as total,COUNT(CASE WHEN is_bind = 1 THEN 1 ELSE NULL END) as bind_count FROM ebikes"

	err = db.Raw(str).Find(&out).Error

	return &out, err
}

func (a *ebike) AppList(ctx context.Context, id int64) ([]*model.EbikeAppListResponse, error) {
	var (
		err    error
		ebikes = make([]*model.EbikeAppListResponse, 0)
	)
	sqlStr := "select e.id,e.name,e.photo,e.color,e.frame_code,e.engine_code,e.sn_code, e.status, et.socket_type, e.battery_id,et.id as device_type_id, " +
		"et.name as device_type_name, ue.share_time,et.image_path,et.image,ue.is_manager,ue.created_at,ct.mac " +
		" from ebikes e,user_ebikes ue ,ebike_types et,controls ct " +
		"where e.id = ue.ebike_id and et.id = e.type_id and e.control_id=ct.id and ue.user_id = ?"
	err = db.Raw(sqlStr, id).Order("ue.created_at desc").Find(&ebikes).Error
	return ebikes, err
}

func (a *ebike) FindByParameter(ctx context.Context, info *model.EbikeInfo) (*entity.Ebike, error) {
	var (
		err error
		out entity.Ebike
		q   = db.Model(&entity.Ebike{})
	)

	if info.ID > 0 {
		q = q.Where("id = ?", info.ID)
	}

	if info.SNCode != "" {
		q = q.Where("sn_code = ?", info.SNCode)
	}
	if info.FrameCode != "" {
		q = q.Where("frame_code = ?", info.FrameCode)
	}
	err = q.First(&out).Error
	return &out, err
}

func (a *ebike) UnBind(ctx context.Context, userId, ebikeId int64, isManager bool) error {
	var (
		err error
	)

	var bikeInfo entity.Ebike
	db.Model(&entity.Ebike{}).Where("id = ?", ebikeId).First(&bikeInfo)
	if bikeInfo.ID == 0 {
		return errors.New("车辆不存在")
	}

	conn := redis.KV.GetRedisPool().Get()
	lock := redis.TRedLock{}
	bSuccess, _ := lock.Lock(conn, fmt.Sprintf("unbind_%v", ebikeId), 10)
	if !bSuccess {
		return fmt.Errorf("绑定加分布式锁失败")
	}
	defer lock.UnLock(conn)

	defer func() {
		trace_log.DebugLogger(bikeInfo.SNCode, "postgres Unbind userId:", userId, "ebikeId:", ebikeId, "isManager:", isManager, "err:", err)
	}()

	err = db.Transaction(func(tx *gorm.DB) error {
		// 删除该辆车的关联信息
		if isManager {
			// 更新车辆表的绑定信息
			err = tx.Model(&entity.Ebike{}).Where("id = ?", ebikeId).Update("is_bind", &model.BindNo).Error
			if err != nil {
				return err
			}

			err = tx.Model(&entity.UserEbike{}).Where("ebike_id = ?", ebikeId).Delete(&entity.UserEbike{}).Error
			if err != nil {
				return err
			}

			// 删除分享表信息
			err = tx.Model(&entity.ShareEbike{}).Where("ebike_id = ?", ebikeId).Delete(&entity.ShareEbike{}).Error

			// 车辆信息
			tx.Model(&entity.Ebike{}).Where("id = ?", ebikeId).First(&bikeInfo)
			if bikeInfo.ID > 0 && bikeInfo.IsBind == 1 {
				trace_log.DebugLogger(bikeInfo.SNCode, "解除绑定失败, isBind = 1")
				return fmt.Errorf("解除绑定失败, isBind = 1")
			}

			var count int64
			tx.Model(&entity.UserEbike{}).Where("ebike_id = ?", ebikeId).Count(&count)
			if count > 0 {
				trace_log.DebugLogger(bikeInfo.SNCode, "解除绑定失败 userBike count > 0")
				return fmt.Errorf("解除绑定失败 userBike count > 0")
			}

			return err
		} else {
			err = tx.Model(&entity.UserEbike{}).Where("ebike_id = ? and user_id = ? and is_manager = ?", ebikeId, userId, 0).Delete(&entity.UserEbike{}).Error
			if err != nil {
				return err
			}
			// 删除分享表信息
			err = tx.Model(&entity.ShareEbike{}).Where("ebike_id = ? and share_user_id = ?", ebikeId, userId).Delete(&entity.ShareEbike{}).Error
		}

		return err
	})

	return err
}

func (a *ebike) Bind(ctx context.Context, sn string, userID int64) error {
	conn := redis.KV.GetRedisPool().Get()
	lock := redis.TRedLock{}
	bSuccess, _ := lock.Lock(conn, fmt.Sprintf("bind_%v", sn), 10)
	if !bSuccess {
		return fmt.Errorf("绑定加分布式锁失败")
	}
	defer lock.UnLock(conn)

	var (
		err     error
		ety     *entity.Ebike
		typeEty entity.EbikeType
		ueEty   entity.UserEbike
		photo   = ""
	)

	if userID == 0 || sn == "" {
		return errors.ParameterErr.Error()
	}

	defer func() {
		trace_log.DebugLogger(sn, "postgres Bind userId:", userID, "err:", err)
	}()

	ety, err = a.FindByParameter(ctx, &model.EbikeInfo{
		SNCode: sn,
	})

	if err != nil {
		return errors.EbikeControlFailed.Error()
	}

	outInfo, _ := grpc.UCmd.UserInfo(ctx, &dto.UserInfoRequest{
		Id: userID,
	})

	if outInfo == nil {
		return fmt.Errorf("用户不存在")
	}

	if !model.IsDebugMode() && !config.IsBindWhite(outInfo.Phone) {
		bCanBind := false
		for i := 0; i < len(ety.BindUsers); i++ {
			if ety.BindUsers[i] == outInfo.Phone {
				bCanBind = true
				break
			}
		}
		if !bCanBind {
			return errors.YouNotInBindList.Error()
		}
	}

	if ety.IsBind == int(model.BindYes) {
		return errors.EbikeAlreadyBound.Error()
	}

	err = db.Transaction(func(tx *gorm.DB) error {
		err = tx.Model(&entity.EbikeType{}).Where("id = ?", ety.TypeID).First(&typeEty).Error
		if err != nil {
			return err
		}
		if typeEty.ImagePath != nil {
			if len(typeEty.ImagePath) > 0 {
				photo = typeEty.ImagePath[0]
			}
		}

		//车辆绑定
		ueEty = entity.UserEbike{
			UserID:    userID,
			EbikeID:   ety.ID,
			ShareTime: -1,
			IsManager: int(model.BindYes),
			Photo:     photo,
			CreatedAt: time.Now().Unix(),
			UpdatedAt: time.Now().Unix(),
		}

		//保存车辆绑定信息
		if err = tx.Model(&entity.UserEbike{}).Create(&ueEty).Error; err != nil {
			return errors.EbikeControlFailed.Error()
		}

		// 更新车辆信息
		if err = tx.Model(&entity.Ebike{}).Where("id = ?", ety.ID).Updates(map[string]interface{}{
			"is_bind":         model.BindYes,
			"activation_time": time.Now().Unix(),
		}).Error; err != nil {
			return errors.EbikeControlFailed.Error()
		}
		return nil
	})

	return err

}

func (a *ebike) FindBySn(ctx context.Context, sn string) (*model.AppEbikeFindBySnResponse, error) {

	type Temp struct {
		Name      string         `json:"name"`
		FrameCode string         `json:"frame_code"`
		IsBind    int            `json:"is_bind"`
		ImagePath pq.StringArray `gorm:"column:image_path" json:"image_path"`
	}
	var (
		err   error
		photo string
		out   *model.AppEbikeFindBySnResponse
		temp  Temp
	)

	err = db.Model(&entity.Ebike{}).Select("ebikes.name,ebikes.frame_code,ebikes.is_bind,ebikes.photo").Where("ebikes.sn_code = ?", sn).First(&temp).Error
	if err != nil {
		return nil, err
	}
	if len(temp.ImagePath) > 0 {
		photo = temp.ImagePath[0]
	}

	out = &model.AppEbikeFindBySnResponse{
		Photo:     photo,
		FrameCode: temp.FrameCode,
		Name:      temp.Name,
		IsBind:    temp.IsBind,
	}
	return out, err
}

func (a *ebike) Upload(c context.Context, ebikes []entity.Ebike, controls []entity.Control, batterys []entity.Battery) error {
	var (
		err error
	)
	err = db.Transaction(func(tx *gorm.DB) error {
		// 批量中控
		err = tx.Model(&entity.Control{}).Create(&controls).Error
		if err != nil {
			return err
		}
		// 批量电池
		err = tx.Model(&entity.Battery{}).Create(&batterys).Error
		if err != nil {
			return err
		}

		// 成功后把车辆ID 循环插入进中控和电池
		for i := range ebikes {
			if i < len(controls) {
				ebikes[i].ControlId = controls[i].Id
			}
			if i < len(batterys) {
				ebikes[i].BatteryId = batterys[i].Id
			}
		}

		// 批量插入车辆
		err = tx.Model(&entity.Ebike{}).Create(&ebikes).Error
		if err != nil {
			return err
		}

		return nil
	})
	return err
}

func (a *ebike) FindBySns(sn []string) ([]int64, error) {
	type Temp struct {
		TypeId int64 `json:"type_id"`
	}
	var (
		err    error
		temp   = make([]*Temp, 0)
		typeId []int64
	)
	err = db.Model(&entity.Ebike{}).Select("distinct  type_id").Where("sn_code in ?", sn).Find(&temp).Error
	if err != nil {
		return nil, err
	}
	for _, t := range temp {
		typeId = append(typeId, t.TypeId)
	}
	return typeId, err
}

func (a *ebike) FindEBikeBySN(ctx context.Context, sn string) (bike entity.Ebike, err error) {
	err = GetDB(ctx).Model(&entity.Ebike{}).Where("sn_code=?", sn).First(&bike).Error
	return
}

func (a *ebike) GetCountByTypeId(ctx context.Context, typeId []int64) (count int64, err error) {
	if len(typeId) == 0 {
		return 0, errors.ParameterErr.Error()
	}
	err = GetDB(ctx).Model(&entity.Ebike{}).Where("type_id in ?", typeId).Count(&count).Error
	return
}
